package com.tools.scanner.ui

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.provider.Settings
import android.widget.FrameLayout
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.IntentSenderRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.ads.AdView
import com.google.android.material.tabs.TabLayout
import com.google.gson.Gson
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.documentscanner.GmsDocumentScanningResult
import com.tools.scanner.BaseActivity
import com.tools.scanner.R
import com.tools.scanner.constant.AdLocationConstant
import com.tools.scanner.ad.AdManager
import com.tools.scanner.bean.BarcodeSchema
import com.tools.scanner.camera.CameraXHelper
import com.tools.scanner.camera.OnCameraCallback
import com.tools.scanner.database.QrHistory
import com.tools.scanner.database.ScannerDatabase
import com.tools.scanner.databinding.ActivityMainBinding
import com.tools.scanner.ui.dialog.ShareDialog
import com.tools.scanner.ui.qrcode.creator.SelectCrateQrCodeActivity
import com.tools.scanner.ui.history.HistoryActivity
import com.tools.scanner.ui.ocr.OcrPreviewActivity
import com.tools.scanner.ui.pdf.ProcessPdfActivity
import com.tools.scanner.ui.qrcode.CodeScannerResultActivity
import com.tools.scanner.utils.AppUtil
import com.tools.scanner.utils.DisplayUtil
import com.tools.scanner.utils.ImageUtil
import com.tools.scanner.utils.QrCodeUtil
import com.tools.scanner.utils.ScannerPdfUtil
import com.tools.scanner.utils.ToastUtil
import com.tools.scanner.viewmodel.DataViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import androidx.core.view.isNotEmpty
import com.tools.scanner.utils.DataReportUtil

class MainActivity : BaseActivity(), OnCameraCallback {

    companion object {
        private const val REQUEST_CAMERA_PERMISSION_CODE = 1002
        private const val REQUEST_IMAGE_CODE = 1003
    }

    private lateinit var mBinding: ActivityMainBinding
    private lateinit var mCameraHelper: CameraXHelper
    private lateinit var mScannerLauncher: ActivityResultLauncher<IntentSenderRequest>
    private lateinit var mAdView: AdView
    private var selectPosition = 0
    private var unSelectPosition = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        DataViewModel.subscribeAppSettingsEvent(this) {
            if (it == DataViewModel.LoadStatus.SUCCESS) {
                loadBannerAd()
            }
        }

        val statusBarHeight = DisplayUtil.getStatusBarHeightStandard(this)
        mBinding.statusBar.post {
            val layoutParams = mBinding.statusBar.layoutParams
            layoutParams.height = statusBarHeight
            mBinding.statusBar.layoutParams = layoutParams
        }

        mBinding.menuTop.post {
            val layoutParams = mBinding.menuTop.layoutParams
            layoutParams.height += statusBarHeight
            mBinding.menuTop.layoutParams = layoutParams
        }

        mCameraHelper = CameraXHelper(this)
        initTab()

        mBinding.ivCapture.setOnClickListener {
            mCameraHelper.takePicture()
        }

        mBinding.ivTorch.setOnClickListener {
            if (mCameraHelper.switchFlash()) {
                mBinding.ivTorch.setImageResource(R.drawable.vector_drawable_torch_light_on)
            } else {
                mBinding.ivTorch.setImageResource(R.drawable.vector_drawable_torch_light_off)
            }
        }

        mBinding.ivGallery.setOnClickListener {
            chooseImage()
        }

        mBinding.ivMenu.setOnClickListener {
            mBinding.drawerLayout.openDrawer(mBinding.menuLayout)
        }

        mBinding.llCreateCode.setOnClickListener {
            SelectCrateQrCodeActivity.actionStart(this@MainActivity)
        }

        mBinding.llHistory.setOnClickListener {
            DataReportUtil.reportClickHistory()
            HistoryActivity.actionStart(this)
        }

        mBinding.llRateUs.setOnClickListener {
            DataReportUtil.reportClickRateUs()
            AppUtil.openGpLink()
        }

        mBinding.llFeedback.setOnClickListener {
            DataReportUtil.reportClickFeedback()
            AppUtil.sendFeedback()
        }

        mBinding.llShare.setOnClickListener {
            DataReportUtil.reportClickShare()
            ShareDialog().show(supportFragmentManager, "")
        }

        mScannerLauncher =
            registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
                if (result.resultCode == RESULT_OK) {
                    val gmsDocumentScanningResult =
                        GmsDocumentScanningResult.fromActivityResultIntent(result.data)

                    if (gmsDocumentScanningResult != null) {
                        ProcessPdfActivity.actionStart(this@MainActivity, gmsDocumentScanningResult)
                    }
                }
            }
    }

    private fun loadBannerAd() {
        if (this::mAdView.isInitialized) {
            return
        }

        mAdView = AdView(this)
        AdManager.loadBannerAd(mAdView, AdLocationConstant.MAIN_BANNER)
    }

    private fun startCamera() {
        mCameraHelper.startCamera(this, this, mBinding.previewView)
    }

    private fun initTab() {
        mBinding.tabLayout.apply {
            addTab(newTab().setText(R.string.scan_text))
            addTab(newTab().setText(R.string.scan_code))
            addTab(newTab().setText(R.string.scan_pdf))

            val listener = object : TabLayout.OnTabSelectedListener {
                override fun onTabSelected(tab: TabLayout.Tab) {
                    selectPosition = tab.position
                    DataReportUtil.reportClickMainTab(selectPosition)

                    when (tab.position) {
                        0 -> {
                            mBinding.ivCapture.isEnabled = true
                            mCameraHelper.setCameraModel(CameraXHelper.CameraModel.OCR)
                        }

                        1 -> {
                            mBinding.ivCapture.isEnabled = false
                            mCameraHelper.setCameraModel(CameraXHelper.CameraModel.QR_CODE)
                        }

                        2 -> {
                            startPdfActivity()
                        }
                    }
                }

                override fun onTabUnselected(tab: TabLayout.Tab) {
                    unSelectPosition = tab.position
                }

                override fun onTabReselected(tab: TabLayout.Tab) {}
            }

            addOnTabSelectedListener(listener)

            mBinding.tabLayout.selectTab(mBinding.tabLayout.getTabAt(1))
        }
    }

    private fun chooseImage() {
        try {
            val intent = Intent(Intent.ACTION_PICK, null)
            intent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*")
            startActivityForResult(intent, REQUEST_IMAGE_CODE)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun startPdfActivity() {
        ScannerPdfUtil.startScan(this, mScannerLauncher)
    }

    private var askedPermission = false

    override fun onResume() {
        super.onResume()
        if (selectPosition == 2) {
            mBinding.tabLayout.selectTab(mBinding.tabLayout.getTabAt(unSelectPosition))
        }
        openCameraWithPermission(true)
        addAdView()
    }

    private fun addAdView() {
        if (mBinding.flAdContainer.isNotEmpty() || !this::mAdView.isInitialized) {
            return
        }
        mBinding.flAdContainer.addView(
            mAdView, FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT,
                FrameLayout.LayoutParams.WRAP_CONTENT
            )
        )
    }

    private fun openCameraWithPermission(isResume: Boolean = false) {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED
        ) {
            startCamera()
        } else {
            if (isResume) {
                if (askedPermission) {
                    return
                }
                askedPermission = true
            }
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.CAMERA),
                REQUEST_CAMERA_PERMISSION_CODE
            )
        }
    }

    override fun onPause() {
        super.onPause()
        mCameraHelper.release()
    }

    private fun showPermissionDialog() {
        AlertDialog.Builder(this)
            .setTitle(R.string.camera)
            .setMessage(R.string.no_permission)
            .setPositiveButton(R.string.enter, object : DialogInterface.OnClickListener {
                override fun onClick(dialog: DialogInterface?, which: Int) {
                    renewRequestPermission()
                }
            }).setNegativeButton(R.string.cancel, object : DialogInterface.OnClickListener {
                override fun onClick(dialog: DialogInterface?, which: Int) {
                    finish()
                }
            })
    }

    private fun renewRequestPermission() {
        val result = ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.CAMERA
        )
        if (result) {
            openCameraWithPermission()
        } else {
            val intent = Intent()
            intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
            val uri = Uri.fromParts("package", packageName, null)
            intent.data = uri
            startActivityForResult(intent, 1002)
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_CAMERA_PERMISSION_CODE) {
            if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startCamera()
            } else {
                showPermissionDialog()
            }
        }
    }

    override fun onCaptureStart() {
        showLoadingDialog()
    }

    @SuppressLint("UnsafeOptInUsageError")
    override fun onCaptureFinish(uri: Uri?) {
        if (uri == null) {
            hideLoadingDialog()
            ToastUtil.showToast(R.string.analyzer_fail)
            return
        }

        hideLoadingDialog()
        OcrPreviewActivity.actionStart(this, uri)
    }

    override fun onScanQrCodeSuccess(barcode: Barcode) {
        showLoadingDialog()
        lifecycleScope.launch(Dispatchers.IO) {
            var schema: BarcodeSchema? = null
            var dataContent: String? = null
            val originalData: String? = barcode.rawValue

            val gson = Gson()
            when (barcode.valueType) {
                Barcode.TYPE_CALENDAR_EVENT -> {
                    if (barcode.calendarEvent != null) {
                        schema = BarcodeSchema.CALENDER
                        dataContent = gson.toJson(barcode.calendarEvent)
                    }
                }

                Barcode.TYPE_CONTACT_INFO -> {
                    if (barcode.contactInfo != null) {
                        schema = BarcodeSchema.CONTACT
                        dataContent = gson.toJson(barcode.contactInfo)
                    }
                }

                Barcode.TYPE_EMAIL -> {
                    if (barcode.email != null) {
                        schema = BarcodeSchema.EMAIL
                        dataContent = gson.toJson(barcode.email)
                    }
                }

                Barcode.TYPE_GEO -> {
                    if (barcode.geoPoint != null) {
                        schema = BarcodeSchema.GEO
                        dataContent = gson.toJson(barcode.geoPoint)
                    }
                }

                Barcode.TYPE_PHONE -> {
                    if (barcode.phone != null) {
                        schema = BarcodeSchema.PHONE
                        dataContent = gson.toJson(barcode.phone)
                    }
                }

                Barcode.TYPE_SMS -> {
                    if (barcode.sms != null) {
                        schema = BarcodeSchema.SMS
                        dataContent = gson.toJson(barcode.sms)
                    }
                }

                Barcode.TYPE_TEXT -> {
                    if (barcode.rawValue != null) {
                        schema = BarcodeSchema.TEXT
                        dataContent = barcode.rawValue
                    }
                }

                Barcode.TYPE_URL -> {
                    if (barcode.url != null) {
                        schema = BarcodeSchema.URL
                        dataContent = gson.toJson(barcode.url)
                    }
                }

                Barcode.TYPE_WIFI -> {
                    if (barcode.wifi != null) {
                        schema = BarcodeSchema.WIFI
                        dataContent = gson.toJson(barcode.wifi)
                    }
                }

                else -> {
                    when (barcode.format) {
                        Barcode.FORMAT_EAN_8 -> {
                            schema = BarcodeSchema.EAN_8
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_EAN_13 -> {
                            schema = BarcodeSchema.EAN_13
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_UPC_E -> {
                            schema = BarcodeSchema.UPC_E
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_UPC_A -> {
                            schema = BarcodeSchema.UPC_A
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_CODE_39 -> {
                            schema = BarcodeSchema.CODE_39
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_CODE_93 -> {
                            schema = BarcodeSchema.CODE_93
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_CODE_128 -> {
                            schema = BarcodeSchema.CODE_128
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_ITF -> {
                            schema = BarcodeSchema.ITF
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_PDF417 -> {
                            schema = BarcodeSchema.PDF_417
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_CODABAR -> {
                            schema = BarcodeSchema.CODABAR
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_DATA_MATRIX -> {
                            schema = BarcodeSchema.DATA_MATRIX
                            dataContent = barcode.rawValue
                        }

                        Barcode.FORMAT_AZTEC -> {
                            schema = BarcodeSchema.AZTEC
                            dataContent = barcode.rawValue
                        }
                    }
                }
            }

            if (schema == null || dataContent == null || originalData == null) {
                withContext(Dispatchers.Main) {
                    DataReportUtil.reportScanCodeFail(originalData ?: "")
                    hideLoadingDialog()
                }
                return@launch
            }

            val qrBitmap = QrCodeUtil.createQrCode(originalData, schema)
            if (qrBitmap == null) {
                withContext(Dispatchers.Main) {
                    hideLoadingDialog()
                }
                return@launch
            }

            val qrFile = ImageUtil.saveToFile(this@MainActivity, qrBitmap)
            if (qrFile == null) {
                withContext(Dispatchers.Main) {
                    hideLoadingDialog()
                }
                return@launch
            }

            val qrHistory = QrHistory(
                schema = schema,
                content = originalData,
                formatContent = dataContent,
                photoPath = qrFile.absolutePath
            )
            ScannerDatabase.get().saveQrHistory(qrHistory)

            withContext(Dispatchers.Main) {
                hideLoadingDialog()
                CodeScannerResultActivity.actionStart(this@MainActivity, qrHistory)
            }

        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_IMAGE_CODE && resultCode == Activity.RESULT_OK && data != null) {
            val uri = data.data ?: return
            if (mCameraHelper.getCameraModel() == CameraXHelper.CameraModel.QR_CODE) {
                processFromFilePath(uri)
            } else {
                onCaptureFinish(uri)
            }
        }
    }

    private fun processFromFilePath(imageUri: Uri) {
        val inputImage: InputImage? = try {
            InputImage.fromFilePath(this, imageUri)
        } catch (e: Exception) {
            e.printStackTrace()
            ImageUtil.uriToBitmap(this, imageUri)?.let {
                InputImage.fromBitmap(it, 0)
            }
        }

        if (inputImage == null) {
            return
        }

        showLoadingDialog()
        QrCodeUtil.processBitmap(inputImage).addOnSuccessListener { result ->

        }.addOnFailureListener {

        }.addOnCompleteListener {
            if (it.isSuccessful && it.result.isNotEmpty()) {
                onScanQrCodeSuccess(it.result[0])
            } else {
                hideLoadingDialog()
            }
        }
    }

    override fun onBackPressed() {
        if (mBinding.drawerLayout.isOpen) {
            mBinding.drawerLayout.closeDrawers()
            return
        }
        super.onBackPressed()
    }
}
